home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / byt86oct.arc / ALLOC.ARC / ALLOC3.MOD < prev    next >
Text File  |  1985-07-12  |  11KB  |  395 lines

  1. IMPLEMENTATION MODULE Alloc3;
  2.  
  3. (* A storage allocator that tries to be safe about freed blocks.
  4.    It uses "handles" (pointers to pointers) to keep track of blocks.
  5.    It also compacts space, and allows resizing.
  6.    Copyright 1986 by Jonathan Amsterdam.  All Rights Reserved. *)
  7.  
  8. FROM SYSTEM IMPORT WORD, ADDRESS, TSIZE, ADR;
  9. FROM MachineSpecific IMPORT getHeapBottom, getHeapTop, bytesPerWord,
  10.     address, cardinal, addrLessThan, writeAddress, addWords, subtractWords,
  11.     maxAddress;
  12. FROM MyTerminal IMPORT fatal, WriteLnString, WriteCard,
  13.     WriteString, WriteLn;
  14.  
  15. CONST maxIndex = 32767;
  16.       nMasters = 10;  (* number of masters to allocate each time more needed *)
  17.  
  18. TYPE handle = POINTER TO blockPtr;
  19.      blockPtr = POINTER TO block;
  20.      block = RECORD
  21.          size:CARDINAL;  (* not including header *)
  22.          CASE BOOLEAN OF
  23.             TRUE: nextBlock: blockPtr;
  24.          |  FALSE: contents:ARRAY[0..maxIndex] OF WORD;
  25.          END;
  26.          END;
  27.  
  28. VAR heapBottom,         (* first word in heap *)
  29.     heapTop,            (* last word in heap *)
  30.     masterPtr,            (* next available master *)
  31.     masterBottom,        (* lowest point of masters section *)
  32.     firstHandle:ADDRESS;    (* first handle ever allocated *)
  33.     freeList:blockPtr;        (* start of free list *)
  34.     blockHeaderWords,        (* # of words in a block header *)
  35.     minBlockSize,        (* smallest value for size field of a block *)
  36.     masterWords:CARDINAL;   (* # of words occupied by a master pointer *)
  37.  
  38. PROCEDURE init;
  39. VAR heapWords:CARDINAL;
  40. BEGIN
  41.     heapBottom := getHeapBottom();
  42.     heapTop := getHeapTop();
  43.     blockHeaderWords := TSIZE(CARDINAL);
  44.     masterWords := TSIZE(ADDRESS);
  45.     minBlockSize := TSIZE(blockPtr);
  46.     freeList := blockPtr(heapBottom);
  47.     heapWords := cardinal(heapTop - heapBottom + address(1)) DIV bytesPerWord;
  48.     freeList^.size := heapWords - blockHeaderWords;
  49.     freeList^.nextBlock := NIL;
  50.     masterBottom := oneAfter(freeList);
  51.     firstHandle := subtractWords(masterBottom, masterWords);
  52.     masterPtr := firstHandle;
  53.     moreMasters;
  54. END init;
  55.  
  56. PROCEDURE oneAfter(blockp:blockPtr):ADDRESS;
  57. (* Returns the address of 1 higher than block *)
  58. BEGIN
  59.     RETURN addWords(blockp, blockp^.size + blockHeaderWords);
  60. END oneAfter;
  61.  
  62.  
  63. PROCEDURE blockSize(h:handle):CARDINAL;
  64. BEGIN
  65.     RETURN h^^.size;
  66. END blockSize;
  67.  
  68. PROCEDURE getWord(h:handle; n:CARDINAL):WORD;
  69. BEGIN
  70.     accessCheck(h, n);
  71.     RETURN h^^.contents[n];
  72. END getWord;
  73.  
  74. PROCEDURE setWord(h:handle; n:CARDINAL; w:WORD);
  75. BEGIN
  76.     accessCheck(h, n);
  77.     h^^.contents[n] := w;
  78. END setWord;
  79.  
  80. PROCEDURE accessCheck(h:handle; n:CARDINAL);
  81. BEGIN
  82.     IF h^ = NIL THEN
  83.     fatal('attempt to access a freed block');
  84.     ELSIF n >= h^^.size THEN
  85.     fatal('access out of bounds');
  86.     END;
  87. END accessCheck;
  88.  
  89. PROCEDURE allocate(nWords:CARDINAL):handle;
  90. VAR master:handle;
  91. BEGIN
  92.     master := allocMaster();
  93.     IF master <> NIL THEN
  94.     master^ := NIL;  (* do this first to prevent this master from
  95.                 being involved in compaction *)
  96.     master^ := allocBlock(nWords);
  97.     END;
  98.     RETURN master;
  99. END allocate;
  100.  
  101. PROCEDURE allocBlock(nWords:CARDINAL):blockPtr;
  102. VAR blockp:blockPtr;
  103. BEGIN
  104.     blockp := allocB(nWords);
  105.     IF blockp = NIL THEN
  106.     compact;
  107.     blockp := allocB(nWords);
  108.     END;
  109.     RETURN blockp;
  110. END allocBlock;
  111.  
  112. PROCEDURE allocB(nWords:CARDINAL):blockPtr;
  113. VAR currBlock, prevBlock, newBlock:blockPtr;
  114.     blockWords:CARDINAL;
  115. BEGIN
  116.     IF nWords < minBlockSize THEN
  117.     nWords := minBlockSize; (* can't allocate a smaller block than this *)
  118.     END;
  119.     blockWords := nWords + blockHeaderWords;
  120.     currBlock := freeList;
  121.     prevBlock := NIL;
  122.     WHILE currBlock <> NIL DO
  123.     IF blockWords + minBlockSize <= currBlock^.size THEN
  124.         (* split the block into two, returning the 1st part *)
  125.         newBlock := addWords(currBlock, blockWords);
  126.         newBlock^.size := currBlock^.size - blockWords;
  127.         newBlock^.nextBlock := currBlock^.nextBlock;
  128.         link(prevBlock, newBlock);
  129.         currBlock^.size := nWords;
  130.         RETURN currBlock;
  131.     ELSIF nWords <= currBlock^.size THEN (* return the whole block *)
  132.         link(prevBlock, currBlock^.nextBlock);
  133.         RETURN currBlock;
  134.     END;
  135.     prevBlock := currBlock;
  136.     currBlock := currBlock^.nextBlock;
  137.     END (* WHILE *);
  138.     RETURN NIL;
  139. END allocB;
  140.  
  141. PROCEDURE allocMaster():handle;
  142. (* The strategy here is as follows:
  143.     1. If there is enough room between masterBottom and masterPtr to allocate
  144.        a master, do so.
  145.     2. If that fails, compact and allocate more masters, then try again.
  146. *)
  147. BEGIN
  148.     IF addrLessThan(masterPtr, masterBottom) THEN
  149.     compact;
  150.     moreMasters;
  151.     END;
  152.     IF addrLessThan(masterPtr, masterBottom) THEN
  153.     RETURN NIL;
  154.     ELSE
  155.     masterPtr := subtractWords(masterPtr, masterWords);
  156.     RETURN addWords(masterPtr, masterWords);
  157.     END;
  158. END allocMaster;
  159.  
  160. PROCEDURE moreMasters;
  161. (* Get highest block.  If its top isn't contiguous with the masters already
  162.    allocated, do nothing.
  163.    Else, try to allocate nMasters from its top; if it's too
  164.    small, allocate it all. 
  165. *)
  166. VAR prev, high:blockPtr;
  167.     nWords:CARDINAL;
  168. BEGIN
  169.     nWords := nMasters * masterWords;
  170.     IF freeList <> NIL THEN
  171.     high := freeList;
  172.     prev := NIL;
  173.     WHILE high^.nextBlock <> NIL DO
  174.         prev := high;
  175.         high := high^.nextBlock;
  176.     END;
  177.     (* high now points to highest block *)
  178.     IF oneAfter(high) = masterBottom THEN
  179.         (* top of block is contiguous with masters *)
  180.         IF high^.size >= minBlockSize + nWords THEN
  181.         (* chop off nWords words from high *)
  182.         DEC(high^.size, nWords);
  183.         masterBottom := oneAfter(high);
  184.         ELSIF high^.size >= minBlockSize + masterWords THEN
  185.         (* chop of enough for one master *)
  186.         DEC(high^.size, masterWords);
  187.         masterBottom := oneAfter(high);
  188.         ELSE
  189.         (* detach whole block *)
  190.         link(prev, high^.nextBlock);
  191.         masterBottom := high;
  192.         END;
  193.     END;
  194.     END;
  195. END moreMasters;
  196.         
  197.     
  198. PROCEDURE free(VAR freeBlock:handle);
  199. BEGIN
  200.     freeBlk(freeBlock^);
  201.     freeBlock^:= NIL;
  202.     freeBlock := NIL;
  203. END free;
  204.  
  205. PROCEDURE freeBlk(freeBlock:blockPtr);
  206. VAR currBlock, prevBlock:blockPtr; 
  207. BEGIN
  208.     IF NOT addrBetween(heapBottom, freeBlock, masterBottom) THEN
  209.     fatal("free: block not in heap");
  210.     ELSIF freeBlock = NIL THEN
  211.     fatal("free: attempt to free an already freed block");
  212.     ELSE
  213.     currBlock := freeList;
  214.     prevBlock := NIL;
  215.     WHILE (currBlock <> NIL) AND addrLessThan(currBlock, freeBlock) DO
  216.         prevBlock := currBlock;
  217.         currBlock := currBlock^.nextBlock;
  218.     END;
  219.     IF currBlock = NIL THEN
  220.         freeBlock^.nextBlock := NIL;
  221.         link(prevBlock, freeBlock);
  222.     ELSE  (* freeBlock belongs just before currBlock *)
  223.         freeBlock^.nextBlock := currBlock;
  224.         link(prevBlock, freeBlock);
  225.     END;
  226.     tryToMerge(prevBlock, freeBlock, currBlock);
  227.     END;
  228. END freeBlk;
  229.  
  230. PROCEDURE tryToMerge(lowBlock, middleBlock, highBlock:blockPtr);
  231. BEGIN
  232.     IF adjacent(middleBlock, highBlock) THEN
  233.     merge(middleBlock, highBlock);
  234.     END;
  235.     IF adjacent(lowBlock, middleBlock) THEN (* this should be impossible *)
  236.     merge(lowBlock, middleBlock);
  237.     END;
  238. END tryToMerge;
  239.  
  240. PROCEDURE adjacent(lowerBlock, higherBlock:blockPtr):BOOLEAN;
  241. BEGIN
  242.   RETURN 
  243.     (lowerBlock <> NIL) AND
  244.     (higherBlock <> NIL) AND 
  245.     (oneAfter(lowerBlock) = higherBlock);
  246. END adjacent;
  247.     
  248. PROCEDURE merge(lowerBlock, higherBlock:blockPtr);
  249. BEGIN
  250.     INC(lowerBlock^.size, higherBlock^.size + blockHeaderWords);
  251.     lowerBlock^.nextBlock := higherBlock^.nextBlock;
  252. END merge;
  253.     
  254. PROCEDURE resize(h:handle; nWords:CARDINAL);
  255. VAR blockp:blockPtr;
  256. BEGIN
  257.     blockp := allocBlock(nWords);
  258.     IF blockp <> NIL THEN
  259.     copyFromTo(h^, blockp, nWords);
  260.     freeBlk(h^);
  261.     h^ := blockp;
  262.     END;
  263. END resize;
  264.  
  265.  
  266. PROCEDURE compact;
  267. (* compact blocks to low end of heap *)
  268. VAR lowPoint:blockPtr;
  269.     lowestHandle:handle;  
  270. BEGIN
  271.   IF freeList <> NIL THEN
  272.     lowPoint := heapBottom;
  273.     WHILE findLowestHandleNotLowerThan(lowPoint, lowestHandle) DO
  274.     IF lowestHandle^ <> lowPoint THEN
  275.         lowPoint^.size := lowestHandle^^.size;
  276.         copyFromTo(lowestHandle^, lowPoint, lowPoint^.size);
  277.         lowestHandle^ := lowPoint;
  278.     END;
  279.     lowPoint := oneAfter(lowPoint);
  280.     END;
  281.     (* now fix freelist *)
  282.     freeList := lowPoint;
  283.     freeList^.size := (cardinal(masterBottom-ADDRESS(freeList)) 
  284.                 DIV bytesPerWord) - blockHeaderWords;
  285.     freeList^.nextBlock := NIL; 
  286.   END;
  287. END compact;
  288.  
  289. PROCEDURE findLowestHandleNotLowerThan(low:blockPtr;VAR min:handle):BOOLEAN;
  290. VAR h:handle;
  291.     return:BOOLEAN;
  292.     bp: blockPtr;
  293. BEGIN
  294.     h := firstHandle;
  295.     bp := blockPtr(maxAddress);
  296.     min := ADR(bp);
  297.     return := FALSE;
  298.     WHILE addrLessThan(masterPtr, h) DO
  299.     IF (NOT addrLessThan(min^, h^)) AND (NOT addrLessThan(h^, low)) THEN
  300.         min := h;
  301.         return := TRUE;
  302.     END;
  303.     h := subtractWords(h, masterWords);
  304.     END;
  305.     RETURN return;
  306. END findLowestHandleNotLowerThan;
  307.  
  308. PROCEDURE copyFromTo(source, dest:blockPtr; nWords:CARDINAL);
  309. VAR i:CARDINAL;
  310. BEGIN
  311.     IF source^.size < nWords THEN
  312.     nWords := source^.size;
  313.     END;
  314.     FOR i := 0 TO nWords-1 DO
  315.     dest^.contents[i] := source^.contents[i];
  316.     END;
  317. END copyFromTo;
  318.  
  319. PROCEDURE link(prevBlock, linkBlock:blockPtr);
  320. BEGIN
  321.     IF prevBlock = NIL THEN
  322.     freeList := linkBlock;
  323.     ELSE
  324.     prevBlock^.nextBlock := linkBlock;
  325.     END;
  326. END link;
  327.  
  328. PROCEDURE addrBetween(low, middle, high:ADDRESS):BOOLEAN;
  329. BEGIN
  330.     RETURN (addrLessThan(low, middle) OR (low = middle)) AND
  331.        (addrLessThan(middle, high) OR (middle = high));
  332. END addrBetween;
  333.  
  334.     (*** debugging stuff ***)
  335.  
  336. PROCEDURE getFreeList():handle;
  337. (* for debugging only *)
  338. BEGIN
  339.     RETURN handle(freeList);
  340. END getFreeList;
  341.  
  342. PROCEDURE writeMap;
  343. VAR lowestFree, lowPoint:blockPtr;
  344.     lowestAlloc:handle;
  345.  
  346.     PROCEDURE writeFree;
  347.     BEGIN
  348.     WriteString("Free  ");
  349.     writeRelAddress(lowestFree);
  350.     WriteCard(lowestFree^.size, 4);
  351.     WriteLnString(" words");
  352.     END writeFree;
  353.  
  354. BEGIN    (* writeMap *)
  355.     WriteLn;
  356.     lowestFree := freeList;
  357.     lowPoint := heapBottom;
  358.     WHILE findLowestHandleNotLowerThan(lowPoint, lowestAlloc) DO
  359.     WHILE addrLessThan(lowestFree, lowestAlloc^) AND (lowestFree <> NIL) DO
  360.         writeFree;
  361.         lowestFree := lowestFree^.nextBlock;
  362.     END;
  363.     WriteString("Alloc ");
  364.     writeRelAddress(lowestAlloc^);
  365.     WriteCard(lowestAlloc^^.size, 4);
  366.     WriteLnString(" words");
  367.     lowPoint := oneAfter(lowestAlloc^);
  368.     END;
  369.     WHILE lowestFree <> NIL DO
  370.     writeFree;
  371.     lowestFree := lowestFree^.nextBlock;
  372.     END;
  373.     WriteLn;
  374.     WriteString("firstHandle:  ");
  375.     writeRelAddress(firstHandle); WriteLn;
  376.     WriteString("masterPtr:    ");
  377.     writeRelAddress(masterPtr); WriteLn;
  378.     WriteString("masterBottom: ");
  379.     writeRelAddress(masterBottom); WriteLn;
  380. END writeMap;
  381.  
  382. PROCEDURE writeRelAddress(a:ADDRESS);
  383. BEGIN
  384.     WriteCard(cardinal(a - heapBottom), 4);
  385. END writeRelAddress;
  386.  
  387. BEGIN
  388.     init;
  389. END Alloc3.
  390. ddress(a:ADDRESS);
  391. BEGIN
  392.     WriteCard(cardinal(a - heapBottom), 4);
  393. END writeRelAddress;
  394.  
  395. BEGI